import csv
import logging
import os
import smtplib
import sys
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import yaml
import pymysql
import openpyxl
from selenium import webdriver
from selenium.common.exceptions import NoSuchElementException, ElementClickInterceptedException
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.select import Select
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC


class Base_Way():
    '''这是一个我们底层所能使用的所有api方法'''

    def __init__(self, browser_type):
        '''
        构造方法
        :param browser_type: 在实例化类的时候，需要填写浏览器的类型，如'Chrome'、'Firefox'
        '''
        if browser_type == 'Chrome' or browser_type == 'C':
            self.driver = webdriver.Chrome()
        elif browser_type == 'Firefox' or browser_type == 'F':
            self.driver = webdriver.Firefox()
        elif browser_type == 'Ie' or browser_type == 'I':
            self.driver = webdriver.Ie()
        self.driver.maximize_window()

    def open(self, url):
        '''
        打开浏览器窗口
        :param url: 需要您指定打开的网址
        :return:
        '''
        self.driver.get(url)

    def find_ele(self, ele):
        '''
        定位页面上某一元素
        :param ele: 定位元素的方法和属性值，格式为:'id,xxx'
        :return: 将定位到的元素返回
        '''
        ele_type = ele.split(',')[0]
        ele_value = ele.split(',')[1]
        if ele_type == 'id' or ele_type == 'i':
            return self.driver.find_element(by=By.ID, value=ele_value)
        elif ele_type == 'name' or ele_type == 'n':
            return self.driver.find_element(by=By.NAME, value=ele_value)
        elif ele_type == 'class' or ele_type == 'c':
            return self.driver.find_element(by=By.CLASS_NAME, value=ele_value)
        elif ele_type == 'tag' or ele_type == 't':
            return self.driver.find_element(by=By.TAG_NAME, value=ele_value)
        elif ele_type == 'link' or ele_type == 'l':
            return self.driver.find_element(by=By.LINK_TEXT, value=ele_value)
        elif ele_type == 'partial' or ele_type == 'p':
            return self.driver.find_element(by=By.PARTIAL_LINK_TEXT, value=ele_value)
        elif ele_type == 'xpath' or ele_type == 'x':
            return self.driver.find_element(by=By.XPATH, value=ele_value)
        elif ele_type == 'css' or ele_type == 's':
            return self.driver.find_element(by=By.CSS_SELECTOR, value=ele_value)

    def close(self):
        '''
        关闭；浏览器
        :return:
        '''
        self.driver.quit()

    def find_eles(self, ele):
        '''
        定位页面上某一元素
        :param ele: 定位元素的方法和属性值，格式为:'id,xxx'
        :return: 将定位到的元素返回
        '''
        ele_type = ele.split(',')[0]
        ele_value = ele.split(',')[1]
        if ele_type == 'id' or ele_type == 'i':
            return self.driver.find_elements(by=By.ID, value=ele_value)
        elif ele_type == 'name' or ele_type == 'n':
            return self.driver.find_elements(by=By.NAME, value=ele_value)
        elif ele_type == 'class' or ele_type == 'c':
            return self.driver.find_elements(by=By.CLASS_NAME, value=ele_value)
        elif ele_type == 'tag' or ele_type == 't':
            return self.driver.find_elements(by=By.TAG_NAME, value=ele_value)
        elif ele_type == 'link' or ele_type == 'l':
            return self.driver.find_elements(by=By.LINK_TEXT, value=ele_value)
        elif ele_type == 'partial' or ele_type == 'p':
            return self.driver.find_elements(by=By.PARTIAL_LINK_TEXT, value=ele_value)
        elif ele_type == 'xpath' or ele_type == 'x':
            return self.driver.find_elements(by=By.XPATH, value=ele_value)
        elif ele_type == 'css' or ele_type == 's':
            return self.driver.find_elements(by=By.CSS_SELECTOR, value=ele_value)

    def boolean_eles(self, ele):
        try:
            self.find_eles(ele)
            return True
        except NoSuchElementException as e:
            print(e)
            return False

    def boolean_ele(self, ele):
        try:
            self.find_ele(ele)
            return True
        except NoSuchElementException as e:
            print(e)
            return False

    def input(self, ele, input_value):
        '''
        向指定元素位置输入指定的内容
        :param ele: 需要定位的元素
        :param input_value: 需要输入的值
        :return:
        '''
        self.find_ele(ele).send_keys(input_value)

    def click(self, ele):
        '''
        此方法可以在页面上执行点击的操作
        :param ele: 需要定位的元素
        :return:
        '''
        self.find_ele(ele).click()

    def switch_frame(self, ele):
        '''
        此方法是切换页面的嵌套层
        :param ele: 需要定位的元素
        :return:
        '''
        ele_frame = self.find_ele(ele)
        self.driver.switch_to.frame(ele_frame)

    def switch_window(self, win_handle_name):
        '''
        切换窗口
        :param win_handle_name: 需要切换到的窗口的句柄
        :return:
        '''
        self.driver.switch_to.window(win_handle_name)

    def get_window(self):
        return self.driver.current_window_handle

    def switch_front_frame(self):
        '''
        返回上一级页面
        :return:
        '''
        self.driver.switch_to.parent_frame()

    def switch_top_frame(self):
        '''
        返回最外层页面
        :return:
        '''
        self.driver.switch_to.default_content()

    def key_event(self, ele, input_value):
        '''
        键盘事件
        :param ele: 需要定位的元素
        :param input_value: 输入键盘事件对应的键或者键和值，格式是:'ENTER'或者'CONTROL,a'
        :return:
        '''
        get_key = Keys.__dict__

        # if ',' not in input_value:
        #     self.find_ele(ele).send_keys(get_key.get(input_value))
        # else:
        #     key_type = input_value.split(",")[0]
        #     key_value = input_value.split(",")[1]
        self.find_ele(ele).send_keys(input_value)

    def mouse_event(self, ele, click_type, drag_ele=None):
        '''
        鼠标事件
        :param ele: 鼠标需要定位的元素
        :param click_type: 执行鼠标事件的种类
        :param drag_ele: 执行拖拽事件时的目标元素，默认值为None
        :return:
        '''
        click_ele = self.find_ele(ele)
        if click_type == 'hold':
            ActionChains(self.driver).click_and_hold(click_ele).perform()
        elif click_type == 'double':
            ActionChains(self.driver).double_click(click_ele).perform()
        elif click_type == 'move':
            ActionChains(self.driver).move_to_element(click_ele).perform()
        elif click_type == 'drag':
            ActionChains(self.driver).drag_and_drop(click_ele, drag_ele).perform()

    def get_attribute(self, ele, attribute_name):
        '''
        获取属性值
        :param ele: 需要定位的元素
        :param attribute_name: 需要获取的属性名称
        :return: 返回获取到的属性值
        '''
        return self.find_ele(ele).get_attribute(attribute_name)

    def get_screenshot(self, filename):
        '''
        截图
        :param filename: 截图保存的名称
        :return:
        '''
        self.driver.get_screenshot_as_file(filename)

    def get_text(self, ele):
        '''
        获取文本的方法
        :param ele: 需要定位的元素
        :return: 返回获取到的文本
        '''
        return self.find_ele(ele).text

    def drop_down(self, ele, select_attribute):
        '''
        下拉框的处理方法
        :param ele:
        :param select_attribute:
        :return:
        '''
        select_type = select_attribute.split(',')[0]
        select_value = select_attribute.split(',')[1]
        ele_dept = self.find_ele(ele)
        if select_type == 'text':
            Select(ele_dept).select_by_visible_text(select_value)
        elif select_type == 'index':
            Select(ele_dept).select_by_index(select_value)
        elif select_type == 'value':
            Select(ele_dept).select_by_value(select_value)

    def show_wait(self, time, ele):
        '''
        显示等待
        :param time: 最大等待的时间
        :param ele: 等待元素
        :return:
        '''
        ele_type = ele.split(',')[0]
        ele_value = ele.split(',')[1]
        if ele_type == 'id' or ele_type == 'i':
            WebDriverWait(self.driver, time).until(EC.presence_of_element_located((By.ID, ele_value)))
        elif ele_type == 'xpath' or ele_type == 'x':
            WebDriverWait(self.driver, time).until(EC.presence_of_element_located((By.XPATH, ele_value)))

    def implicit_wait(self, time):
        '''
        隐式等待
        :param time: 等待最长时间
        :return:
        '''
        self.driver.implicitly_wait(time)

    def execute_script(self, js_code):
        self.driver.execute_script(js_code)

    def window_handles_new(self):
        # 切换到新出现的窗口
        windows = self.driver.window_handles
        self.driver.switch_to.window(windows[-1])



class PublicDriver():
    def __init__(self, driver: Base_Way):  # 我这个参数只接收RanZhi_Way类实例化后的对象
        self.driver = driver


class GetExcel():
    '''读取excel文件数据的方法'''

    def getexcel(self, excel_path, sheetname):
        '''
        获取指定的excel文件的数据
        :param excel_path: excel文件的路径
        :param sheetname: excel中sheet名
        :return: 此返回值为获取到的excel表的值，格式是:[(a,b,c,...),(d,e,...),.....]
        '''
        excel_file = openpyxl.load_workbook(excel_path)
        excel_data = excel_file[sheetname]
        sheet_data = []
        skip = True
        for i in excel_data:
            if skip:
                skip = False
                continue
            cell_data = []
            for j in i:
                if j.value == None:
                    j.value = ''
                cell_data.append(j.value)
            sheet_data.append(tuple(cell_data))
        return sheet_data


class ReadYaml():
    def readyaml(self, filepath):
        '''
        读取yaml文件数据
        :param filepath: yaml路径
        :return: 返回yaml文件的内容,返回的格式是字典嵌套字典，比如:{xxx:{xx:x}}
        '''
        with open(filepath, encoding='utf8') as yaml_file:
            return yaml.safe_load(yaml_file)


class GetCsv():
    def getcsv(self, csv_path):
        '''
        获取csv文件数据
        :param csv_path:
        :return:
        '''
        data_csv = []
        with open(csv_path, encoding='utf8') as csv_file:
            csv_data = csv.reader(csv_file)
            for i in csv_data:
                data_csv.append(tuple(i))
            return data_csv[1:]


class DB_Connect():
    def dbconnect(self, host, user, password, port, database, sql):
        # 连接数据库
        db = pymysql.Connection(host=host, user=user, password=password, port=port, database=database)
        # 建立数据库游标
        cursor = db.cursor()
        # 数据库执行操作
        cursor.execute(sql)
        dbdata = cursor.fetchall()
        cursor.close()
        return dbdata


class SendLog:
    def __init__(self, log_path):
        self.file_name = log_path
        self.logger = logging.getLogger()
        # 设置日志的级别
        self.logger.setLevel(logging.DEBUG)
        # 设置日志的格式与内容
        self.formatter = logging.Formatter('[%(asctime)s]-[%(filename)s]-[%(levelname)s]:%(message)s')

    def _console(self, level, message):
        # 创建一个FileHandler对象，把日志内容追加到末尾
        fh = logging.FileHandler(self.file_name, 'a', 'utf8')
        fh.setLevel(logging.DEBUG)
        # 设置文件内容的格式
        fh.setFormatter(self.formatter)
        # 将内容添加到日志文件
        self.logger.addHandler(fh)

        # 创建StreamHandler对象，把日志输出到控制台
        ch = logging.StreamHandler(sys.stdout)
        ch.setLevel(logging.DEBUG)
        ch.setFormatter(self.formatter)
        self.logger.addHandler(ch)

        if level == 'info':
            self.logger.info(message)
        elif level == 'debug':
            self.logger.debug(message)
        elif level == 'warning':
            self.logger.warning(message)
        elif level == 'error':
            self.logger.error(message)

        # 避免日志输出重复
        self.logger.removeHandler(fh)
        self.logger.removeHandler(ch)
        fh.close()

    def info(self, message):
        self._console('info', message)

    def debug(self, message):
        self._console('debug', message)

    def warning(self, message):
        self._console('warning', message)

    def error(self, message):
        self._console('error', message)


class SendEmail():
    def sendemail(self, report_path, server, port, sender, pwd, receiver, subject):
        '''配置邮件服务器信息'''
        smtpserver = server
        port = port

        sender = sender
        pwd = pwd
        receiver = receiver

        # 创建邮件对象
        msg = MIMEMultipart()
        # 发送人、收件人、邮件的主题
        msg['from'] = sender
        # 有多个收件人时，以分好隔开
        msg['to'] = ';'.join(receiver)
        msg['subject'] = subject

        # 读取测试报告内容
        with open(report_path, 'rb') as rf:
            body = rf.read()

        # 写邮件的正文
        mine_text = MIMEText(body, 'html', 'utf8')
        msg.attach(mine_text)

        # 写附件内容
        att = MIMEText(body, 'base64', 'utf8')
        att['Content-type'] = 'application/octet-stream'
        att['Content-Disposition'] = 'attachment;filename="%s"' % report_path
        msg.attach(att)

        # 发送邮件
        smtp = smtplib.SMTP()
        smtp.connect(smtpserver, port)
        smtp.login(sender, pwd)
        smtp.sendmail(sender, receiver.split(';'), msg.as_string())
        smtp.close()
        print('发送邮件成功！')


class GetPath:
    def getpath(self, project_name='ranzhi', relative_path=None):
        '''
        获取项目路径
        :param project_name: 项目名称
        :param relative_path: 相对路径，要求从项目名称处开始写
        :return:
        '''
        current_abs_path = os.path.dirname(__file__)
        local_path = current_abs_path.split(project_name)[0]
        return local_path + relative_path
